package com.vains.io;

import io.jsonwebtoken.*;

import java.util.*;

/**
 * <p>
 *  基于java实现的json web token工具类
 * </p>
 *
 * @author vains
 * @since 2020-07-06
 */
public class JwtUtils {

    private JwtUtils() {}

    /**
     * 根据用户ID生成token
     * @param userId 用户ID
     * @param ttlMillis 有效毫秒数 当为-1时无过期时间
     * @param secretKey 生成签名的秘钥
     * @return 返回token
     */
    public static String createToken(String secretKey, Integer userId, long ttlMillis) {
        return createToken(userId, ttlMillis, secretKey);
    }

    /**
     * 根据参数 data 生成token
     * @param data 放进token中的数据
     * @param ttlMillis 有效毫秒数 当为-1时无过期时间
     * @param secretKey 生成签名的秘钥
     * @return 返回token
     */
    public static String createToken(long ttlMillis, Map<String, Object> data, String secretKey) {
        return createToken(data, ttlMillis, secretKey);
    }

    /**
     * 根据参数 data 生成token
     * @param data 放进token中的数据
     * @param ttlMillis 有效毫秒数 当为-1时无过期时间
     * @param secretKey 生成签名的秘钥
     * @return 返回token
     */
    public static String createToken(Object data, long ttlMillis, String secretKey) {
        long currentTimeMillis = System.currentTimeMillis();
        //指定签名的时候使用的签名算法，也就是header那部分，j jwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        JwtBuilder builder = Jwts.builder();
        // 设置签发时间
        builder.setIssuedAt(new Date(currentTimeMillis));
        // 设置ID, jwt的唯一标识
        builder.setId(UUID.randomUUID().toString());
        // jwt的主体, 存放json字符串
        String jsonData = JsonUtils.objectCovertToJson(data);
        builder.setSubject(jsonData);
        // 签名生成算法和秘钥
        builder.signWith(signatureAlgorithm, secretKey);
        if (ttlMillis >= 0) {
            // 过期时间设置
            long expMillis = currentTimeMillis + ttlMillis;
            builder.setExpiration(new Date(expMillis));
        }
        return builder.compact();
    }

    /**
     * 解析token
     * @param token 要解析的token
     * @param secretKey 秘钥
     * @return 返回 载荷,自定义数据
     */
    public static Claims parseToken(String token, String secretKey) {
        // 获取jwt解析器
        JwtParser parser = Jwts.parser();
        // 设置秘钥
        parser.setSigningKey(secretKey);
        try {
            // 解析token
            return parser.parseClaimsJws(token).getBody();
        } catch (SignatureException e) {
            throw new IllegalArgumentException("秘钥错误, token解析失败!");
        } catch (ExpiredJwtException e) {
            throw new IllegalArgumentException("token已过期, token解析失败!");
        } catch (MalformedJwtException e) {
            throw new IllegalArgumentException("token无效, token解析失败!");
        }
    }

    /**
     * 从token中获取UserId
     * 需要保证token中只是 UserID
     * @param token token
     * @param secretKey 秘钥
     * @return 返回用户ID
     */
    public static Integer getUserId(String token, String secretKey) {
        String subject = parseToken(token, secretKey).getSubject();
        return Integer.valueOf(subject);
    }

    /**
     * 从token中获取Subject, 也就是自定义的数据
     * @param token token
     * @param secretKey 秘钥
     * @return 返回用户ID
     */
    public static Map<String, Object> getSubject(String token, String secretKey) {
        String subject = parseToken(token, secretKey).getSubject();
        return JsonUtils.objectCovertToObject(subject, Map.class, String.class, Object.class);
    }

    /**
     * 从token中获取Subject, 也就是自定义的数据
     * @param token token
     * @param secretKey 秘钥
     * @param collectionClazz 集合类型
     * @param clazz 集合泛型的类型
     * @param <T> 泛型
     * @return 返回collectionClazz<clazz> 实例
     */
    public static <T> T getListSubject(String token, String secretKey, Class<?> collectionClazz, Class<?>... clazz) {
        String subject = parseToken(token, secretKey).getSubject();
        return JsonUtils.objectCovertToObject(subject, collectionClazz, clazz);
    }

    /**
     * 从token中获取Subject, 也就是自定义的数据
     * @param token token
     * @param secretKey 秘钥
     * @param clazz 解析出来的类型
     * @param <T> 泛型
     * @return 返回clazz对应的实例
     */
    public static <T> T getObjectSubject(String token, String secretKey, Class<T> clazz) {
        String subject = parseToken(token, secretKey).getSubject();
        return JsonUtils.jsonCovertToObject(subject, clazz);
    }

}
